/**
 * 
 */
package com.thinkgsp.jxc.service.impl;

import java.math.BigDecimal;
import java.sql.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.thinkgsp.jxc.model.Customer;
import com.thinkgsp.jxc.model.RpAPayBill;
import com.thinkgsp.jxc.model.RpARecBill;
import com.thinkgsp.jxc.model.RpArpBill;
import com.thinkgsp.jxc.model.RpBill;
import com.thinkgsp.jxc.model.RpBillCheck;
import com.thinkgsp.jxc.model.RpBillCheckLog;
import com.thinkgsp.jxc.model.RpBillDetail;
import com.thinkgsp.jxc.model.RpContact;
import com.thinkgsp.jxc.model.RpContactBalance;
import com.thinkgsp.jxc.model.RpPayBill;
import com.thinkgsp.jxc.model.RpRecBill;
import com.thinkgsp.jxc.persistence.CustomerMapper;
import com.thinkgsp.jxc.persistence.RpCheckLogMapper;
import com.thinkgsp.jxc.persistence.mybatis.PagingResult;
import com.thinkgsp.jxc.service.RpBillCheckService;
import com.thinkgsp.jxc.service.ServiceException;
import com.thinkgsp.sys.model.SysAccountPeriod;
import com.thinkgsp.util.PeriodUtil;

/**
 * @author evan
 *
 */
@Service("billCheckService")
@Transactional
public class RpBillCheckServiceImpl extends RpServiceImpl implements
		RpBillCheckService {
	@Autowired
	private RpCheckLogMapper checkLogMapper;
	@Autowired
	private CustomerMapper custMapper;

	public void setCheckLogMapper(RpCheckLogMapper checkLogMapper) {
		this.checkLogMapper = checkLogMapper;
	}
	
	public void setCustomerMapper(CustomerMapper custMapper) {
		this.custMapper = custMapper;
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#findCheckLog(java.sql.Date, java.sql.Date, java.lang.Integer)
	 */
	public List<RpBillCheckLog> findCheckLog(Integer direct,Date begDate, Date endDate,
			Integer custId) {
		return checkLogMapper.getCheckLogListByDate(direct, begDate, endDate, custId);
	}

	public PagingResult<RpBillCheckLog> findCheckLog(Integer direct,
			Date begDate, Date endDate, Integer custId, int start, int size) {
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("direct", direct);
		params.put("begDate", begDate);
		params.put("endDate", endDate);
		if(custId != null) {
			params.put("custId", custId);
		}
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.RpCheckLogMapper.getCheckLogListByDateCount", 
				"com.thinkgsp.jxc.persistence.RpCheckLogMapper.getCheckLogListByDatePaging", 
				params, rows);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#findCheckLog(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer)
	 */
	public List<RpBillCheckLog> findCheckLog(Integer direct,Integer begYear,
			Integer begPeriod, Integer endYear, Integer endPeriod,
			Integer custId) {
		BigDecimal bp = PeriodUtil.convertYearPeriodToDecimal(begYear, begPeriod);
		BigDecimal ep = PeriodUtil.convertYearPeriodToDecimal(endYear, endPeriod);
		return checkLogMapper.getCheckLogListByPeriod(direct, bp, ep, custId);
	}
	
	public PagingResult<RpBillCheckLog> findCheckLog(Integer direct,
			Integer begYear, Integer begPeriod, Integer endYear,
			Integer endPeriod, Integer custId, int start, int size) {
		BigDecimal bp = PeriodUtil.convertYearPeriodToDecimal(begYear, begPeriod);
		BigDecimal ep = PeriodUtil.convertYearPeriodToDecimal(endYear, endPeriod);
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("direct", direct);
		params.put("begPeriod", bp);
		params.put("endPeriod", ep);
		if(custId != null) {
			params.put("custId", custId);
		}
		RowBounds rows = new RowBounds(start, size);
		return queryPagingHandler.queryPagingResult(
				"com.thinkgsp.jxc.persistence.RpCheckLogMapper.getCheckLogListByPeriodCount", 
				"com.thinkgsp.jxc.persistence.RpCheckLogMapper.getCheckLogListByPeriodPaging", 
				params, rows);
	}
	
	public Long autoCheckRecBill(Long recBillId) throws ServiceException {
		RpRecBill recBill = rpBillMapper.getRpBill(recBillId);
		if(!recBill.getArchived()) {
			throw new ServiceException("本单据未归档，不允许自动核销。");
		}
		if(recBill.getDetails().size() == 0) {
			throw new ServiceException("本单据没有引用应收单,不能自动核销。");
		}
		recBill.setCurrentCheckAmount(recBill.getAmount().add(recBill.getDiscount()));
		List<RpARecBill> arecBills = new ArrayList<RpARecBill>();
		for(RpBillDetail detail : recBill.getDetails()) {
			RpARecBill arecBill = (RpARecBill)detail.getArpBill();
			arecBill.setCurrentCheckAmount(detail.getSettleAmount().add(detail.getDiscount()));
			arecBills.add(arecBill);
		}
		List<RpRecBill> recBills = new ArrayList<RpRecBill>();
		recBills.add(recBill);
		return checkForRecBill(recBills, arecBills);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForRecBill(java.util.List, java.util.List)
	 */
	public Long checkForRecBill(List<RpRecBill> recBills,
			List<RpARecBill> arecBills) throws ServiceException {
		
		List<RpBillCheck> rpbills = new ArrayList<RpBillCheck>();
		rpbills.addAll(recBills);
		List<RpBillCheck> arpbills = new ArrayList<RpBillCheck>();
		arpbills.addAll(arecBills);
		if(!validateCheckAmount(arpbills, rpbills,false)) {
			return 0L;
		}
		Long checkId = newCheckId();
		SysAccountPeriod period = getCurrentPeriod();
		for(RpRecBill recBill : recBills) {
			//应收应付单据不再记录核销信息
			//rpBillMapper.billCheck(recBill.getRpBillId(), recBill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByRpBill(recBill.getRpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：收款单" + recBill.getRpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), recBill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_RECBILL, recBill, contact.getId(), period);
		}
		for(RpARecBill arecBill : arecBills) {
			//应收应付单据不再记录核销信息
			//arpBillMapper.billCheck(arecBill.getArpBillId(), arecBill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByArpBill(arecBill.getArpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：应收单" + arecBill.getArpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), arecBill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_RECBILL, arecBill, contact.getId(), period);
		}
		return checkId;
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForRefundRecBill(java.util.List, java.util.List)
	 */
	public Long checkForRefundRecBill(List<RpRecBill> refundRecBills,
			List<RpRecBill> recBills) throws ServiceException {
		List<RpBillCheck> refundbills = new ArrayList<RpBillCheck>();
		refundbills.addAll(refundRecBills);
		List<RpBillCheck> recbills = new ArrayList<RpBillCheck>();
		recbills.addAll(recBills);
		if(!validateCheckAmount(refundbills, recbills,true)) {
			return 0L;
		}
		
		Long checkId = newCheckId();
		SysAccountPeriod period = getCurrentPeriod();
		
		for(RpRecBill refundbill : refundRecBills) {
			//rpBillMapper.billCheck(refundbill.getRpBillId(), refundbill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByRpBill(refundbill.getRpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：退款单" + refundbill.getRpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), refundbill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_REFUND_RECBILL, refundbill, contact.getId(), period);
		}
		for(RpRecBill recBill : recBills) {
			//rpBillMapper.billCheck(recBill.getRpBillId(), recBill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByRpBill(recBill.getRpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：收款单" + recBill.getRpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), recBill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_REFUND_RECBILL, recBill, contact.getId(), period);
		}
		return checkId;
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForArecBillOffsetApayBill(java.util.List, java.util.List)
	 */
	public Long checkForArecBillOffsetApayBill(List<RpARecBill> arecBills,
			List<RpAPayBill> apayBills) throws ServiceException {
		
		List<RpBillCheck> arecbills = new ArrayList<RpBillCheck>();
		arecbills.addAll(arecBills);
		List<RpBillCheck> apaybills = new ArrayList<RpBillCheck>();
		apaybills.addAll(apayBills);
		if(!validateCheckAmount(arecbills, apaybills, false)) {
			return 0L;
		}
		
		Long checkId = newCheckId();
		SysAccountPeriod period = getCurrentPeriod();
		for(RpARecBill arecbill : arecBills) {
			//arpBillMapper.billCheck(arecbill.getArpBillId(), arecbill.getCurrentCheckAmount());
			RpContact linkcontact = contactMapper.findRpContactByArpBill(arecbill.getArpBillId());
			if(linkcontact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：应收单" + arecbill.getArpBillCode() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(linkcontact.getId(), arecbill.getCurrentCheckAmount());
			//插入往来表的冲抵应收款记录
			RpContact contact = new RpContact();
			contact.setDirect(1);
			contact.setTypeId(7);
			contact.setBillDate(new Date(System.currentTimeMillis()));
			contact.setCustSuppId(arecbill.getCustSuppId());
			contact.setDeptId(arecbill.getDeptId());
			contact.setEmpId(arecbill.getEmpId());
			contact.setAmount(arecbill.getCurrentCheckAmount());
			contact.setCheckStatus(1);
			contact.setRemAmount(BigDecimal.ZERO);
			contact.setFyear(period.getFyear());
			contact.setPeriod(period.getPeriod());
			contact.setRemark("应收款冲抵应付款，对应单据:" + arecbill.getArpBillCode());
			contactMapper.insertContact(contact);
			
			RpContactBalance balance = this.findContactBalance(1, period.getFyear(), period.getPeriod(), arecbill.getCustSuppId(), arecbill.getDeptId(), arecbill.getEmpId());
			if(balance == null) {
				throw new ServiceException("核销失败，原因：未检索到客户应收余额表的往来余额。");
			}
			cbMapper.recordAccountsReceivableForCredit(balance.getId(), arecbill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_ARECBILL_OFFSET_APAYBILL, arecbill, contact.getId(), period);
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_ARECBILL_OFFSET_APAYBILL, linkcontact, period);
		}
		
		for(RpAPayBill apaybill : apayBills) {
			//arpBillMapper.billCheck(apaybill.getArpBillId(), apaybill.getCurrentCheckAmount());
			RpContact linkcontact = contactMapper.findRpContactByArpBill(apaybill.getArpBillId());
			if(linkcontact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：应付单" + apaybill.getArpBillCode() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(linkcontact.getId(), apaybill.getCurrentCheckAmount());
			
			RpContact contact = new RpContact();
			contact.setDirect(-1);
			contact.setTypeId(-7);
			contact.setBillDate(new Date(System.currentTimeMillis()));
			contact.setCustSuppId(apaybill.getCustSuppId());
			contact.setDeptId(apaybill.getDeptId());
			contact.setEmpId(apaybill.getEmpId());
			contact.setAmount(apaybill.getCurrentCheckAmount());
			contact.setCheckStatus(1);
			contact.setRemAmount(BigDecimal.ZERO);
			contact.setFyear(period.getFyear());
			contact.setPeriod(period.getPeriod());
			contact.setRemark("应收款冲抵应付款，对应单据:" + apaybill.getArpBillCode());
			contactMapper.insertContact(contact);
			
			RpContactBalance balance = this.findContactBalance(-1, period.getFyear(), period.getPeriod(), apaybill.getCustSuppId(), apaybill.getDeptId(), apaybill.getEmpId());
			if(balance == null ){
				throw new ServiceException("核销失败，原因：未检索到客户应付余额表的往来余额。");
			}
			cbMapper.recordAccountsPayableForDebit(balance.getId(), apaybill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_ARECBILL_OFFSET_APAYBILL, apaybill, contact.getId(), period);
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_ARECBILL_OFFSET_APAYBILL, linkcontact, period);
		}
		return checkId;
	}
	
	public Long checkForArecBillTransfer(RpARecBill arecBill,Integer custSuppId) throws ServiceException {
		RpARecBill transferBill = new RpARecBill();
		SysAccountPeriod period = getCurrentPeriod();
		Customer customer = custMapper.getCust(custSuppId);
		transferBill.setAmount(arecBill.getAmount());
		transferBill.setArpDate(arecBill.getArpDate());
		transferBill.setBillDate(new Date(System.currentTimeMillis()));
		transferBill.setCurrentCheckAmount(arecBill.getCurrentCheckAmount());
		transferBill.setCustomer(customer);
		transferBill.setCustSuppId(custSuppId);
		transferBill.setDept(arecBill.getDept());
		transferBill.setDeptId(arecBill.getDeptId());
		transferBill.setEmp(arecBill.getEmp());
		transferBill.setEmpId(arecBill.getEmpId());
		transferBill.setFuncDate(new Date(System.currentTimeMillis()));
		transferBill.setFyear(period.getFyear());
		transferBill.setPeriod(period.getPeriod());
		transferBill.setMakerId(getCurrentUser().getEmpId());
		transferBill.setArpBillCode(newNumber(RpARecBill.class));
		String outCustName = "";
		if(arecBill.getCustomer() != null) {
			outCustName = "【" + arecBill.getCustomer().getCustomerCode() + "】" + arecBill.getCustomer().getCustomerName();
		} else {
			outCustName = arecBill.getCustSuppId().toString();
		}
		transferBill.setRemark("由客户:" + outCustName +  " 的应收单转入,原单据:" + arecBill.getArpBillCode());
		arpBillMapper.insertArpBill(transferBill);
		//saveArpBill(transferBill);
		archiveARecBill(transferBill.getArpBillId());
		
		Long checkId = checkForArecBillTransfer(arecBill, transferBill);
		return checkId;
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForArecBillTransfer(java.util.List, com.thinkgsp.jxc.model.RpARecBill)
	 */
	private Long checkForArecBillTransfer(RpARecBill arecBill,RpARecBill transferBill) throws ServiceException {
		List<RpBillCheck> arecbills = new ArrayList<RpBillCheck>();
		arecbills.add(arecBill);
		List<RpBillCheck> transferCheck = new ArrayList<RpBillCheck>();
		transferCheck.add(transferBill);
		if(!validateCheckAmount(arecbills, transferCheck, false)) {
			throw new ServiceException("核销金额检查不通过。");
		}
		//Long checkId = newCheckId();
		SysAccountPeriod period = getCurrentPeriod();
		
		RpRecBill recBill = new RpRecBill();
		recBill.setRpBillCode(newNumber(RpRecBill.class));
		recBill.setCustomer(arecBill.getCustomer());
		recBill.setCustSuppId(arecBill.getCustSuppId());
		recBill.setDeptId(arecBill.getDeptId());
		recBill.setEmpId(arecBill.getEmpId());
		recBill.setMakerId(getCurrentUser().getEmpId());
		recBill.setAmount(arecBill.getCurrentCheckAmount());
		recBill.setFyear(period.getFyear());
		recBill.setPeriod(period.getPeriod());
		recBill.setRemark("应收单转销到客户【" + transferBill.getCustomer().getCustomerCode() + "】" + transferBill.getCustomer().getCustomerName());
		RpBillDetail detail = new RpBillDetail();
		detail.setArpBillId(arecBill.getArpBillId());
		detail.setSettleAmount(arecBill.getCurrentCheckAmount());
		recBill.getDetails().add(detail);
		saveRecBill(recBill);
		if(!recBill.getArchived()) {
			archiveRecBill(recBill.getRpBillId());
		}
		recBill.setCurrentCheckAmount(arecBill.getCurrentCheckAmount());
		
		Long checkId = newCheckId();
		//核销收款单
		RpContact contact = contactMapper.findRpContactByRpBill(recBill.getRpBillId());
		if(contact == null) {
			throw new ServiceException("更新往来明细的核销金额出错，原因：收款单" + recBill.getRpBillId() + "未检索到对应的往来明细。");
		}
		contactMapper.billCheck(contact.getId(), recBill.getCurrentCheckAmount());
		createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_RECBILL, recBill, contact.getId(), period);
		//核销应收单
		contact = contactMapper.findRpContactByArpBill(arecBill.getArpBillId());
		if(contact == null) {
			throw new ServiceException("更新往来明细的核销金额出错，原因：应收单" + arecBill.getArpBillId() + "未检索到对应的往来明细。");
		}
		contactMapper.billCheck(contact.getId(), arecBill.getCurrentCheckAmount());
		createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_RECBILL, arecBill, contact.getId(), period);
			
		contact = contactMapper.findRpContactByArpBill(transferBill.getArpBillId());
		createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_ARECBILL_TRANSFER, transferBill, contact.getId(), period);
		return checkId;
	}
	
	public Long autoCheckPayBill(Long billId) throws ServiceException {
		RpPayBill payBill = rpBillMapper.getRpBill(billId);
		if(!payBill.getArchived()) {
			throw new ServiceException("单据未归档,不允许核销。");
		}
		if(payBill.getDetails().size() == 0) {
			throw new ServiceException("本单据没有引用应付单，不允许自动核销。");
		}
		payBill.setCurrentCheckAmount(payBill.getAmount().add(payBill.getDiscount()));
		List<RpAPayBill> apayBills = new ArrayList<RpAPayBill>();
		for(RpBillDetail detail : payBill.getDetails()) {
			RpAPayBill apayBill = (RpAPayBill)detail.getArpBill();
			apayBill.setCurrentCheckAmount(detail.getSettleAmount().add(detail.getDiscount()));
			apayBills.add(apayBill);
		}
		List<RpPayBill> payBills = new ArrayList<RpPayBill>(1);
		payBills.add(payBill);
		return checkForPayBill(payBills, apayBills);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForPayBill(java.util.List, java.util.List)
	 */
	public Long checkForPayBill(List<RpPayBill> payBills,
			List<RpAPayBill> apayBills) throws ServiceException {
		List<RpBillCheck> rpbills = new ArrayList<RpBillCheck>();
		rpbills.addAll(payBills);
		List<RpBillCheck> arpbills = new ArrayList<RpBillCheck>();
		arpbills.addAll(apayBills);
		if(!validateCheckAmount(arpbills, rpbills,false)) {
			return 0L;
		}
		Long checkId = newCheckId();
		SysAccountPeriod period = getCurrentPeriod();
		
		for(RpPayBill payBill : payBills) {
			//rpBillMapper.billCheck(payBill.getRpBillId(), payBill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByRpBill(payBill.getRpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：付款单" + payBill.getRpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), payBill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_PAYBILL, payBill, contact.getId(), period);
		}
		
		for(RpAPayBill apayBill : apayBills) {
			//arpBillMapper.billCheck(apayBill.getArpBillId(), apayBill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByArpBill(apayBill.getArpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：应付单" + apayBill.getArpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), apayBill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_PAYBILL, apayBill, contact.getId(), period);
		}
		return checkId;
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForRefundPayBill(java.util.List, java.util.List)
	 */
	public Long checkForRefundPayBill(List<RpPayBill> refundPayBills,
			List<RpPayBill> payBills) throws ServiceException {
		List<RpBillCheck> refundbills = new ArrayList<RpBillCheck>();
		refundbills.addAll(refundPayBills);
		List<RpBillCheck> recbills = new ArrayList<RpBillCheck>();
		recbills.addAll(payBills);
		if(!validateCheckAmount(refundbills, recbills,true)) {
			return 0L;
		}
		
		Long checkId = newCheckId();
		SysAccountPeriod period = getCurrentPeriod();
		
		for(RpPayBill refundbill : refundPayBills) {
			//rpBillMapper.billCheck(refundbill.getRpBillId(), refundbill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByRpBill(refundbill.getRpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：退款单" + refundbill.getRpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), refundbill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_REFUND_PAYBILL, refundbill, contact.getId(), period);
		}
		for(RpPayBill payBill : payBills) {
			//rpBillMapper.billCheck(payBill.getRpBillId(), payBill.getCurrentCheckAmount());
			RpContact contact = contactMapper.findRpContactByRpBill(payBill.getRpBillId());
			if(contact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：收款单" + payBill.getRpBillId() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(contact.getId(), payBill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_REFUND_PAYBILL, payBill, contact.getId(), period);
		}
		return checkId;
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForApayBillOffsetArecBill(java.util.List, java.util.List)
	 */
	public Long checkForApayBillOffsetArecBill(List<RpAPayBill> apayBills,
			List<RpARecBill> arecBills) throws ServiceException {
		List<RpBillCheck> arecbills = new ArrayList<RpBillCheck>();
		arecbills.addAll(arecBills);
		List<RpBillCheck> apaybills = new ArrayList<RpBillCheck>();
		apaybills.addAll(apayBills);
		if(!validateCheckAmount(apaybills, arecbills, false)) {
			return 0L;
		}
		
		Long checkId = newCheckId();
		SysAccountPeriod period = getCurrentPeriod();
		
		for(RpARecBill arecbill : arecBills) {
			//arpBillMapper.billCheck(arecbill.getArpBillId(), arecbill.getCurrentCheckAmount());
			RpContact linkcontact = contactMapper.findRpContactByArpBill(arecbill.getArpBillId());
			if(linkcontact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：应收单" + arecbill.getArpBillCode() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(linkcontact.getId(), arecbill.getCurrentCheckAmount());
			
			//插入往来表的冲抵应收款记录
			RpContact contact = new RpContact();
			contact.setDirect(1);
			contact.setTypeId(7);
			contact.setBillDate(new Date(System.currentTimeMillis()));
			contact.setCustSuppId(arecbill.getCustSuppId());
			contact.setDeptId(arecbill.getDeptId());
			contact.setEmpId(arecbill.getEmpId());
			contact.setAmount(arecbill.getCurrentCheckAmount());
			contact.setCheckStatus(1);
			contact.setRemAmount(BigDecimal.ZERO);
			contact.setFyear(period.getFyear());
			contact.setPeriod(period.getPeriod());
			contact.setRemark("应付款冲抵应收款，对应单据:" + arecbill.getArpBillCode());
			contactMapper.insertContact(contact);
			
			RpContactBalance balance = this.findContactBalance(1, period.getFyear(), period.getPeriod(), arecbill.getCustSuppId(), arecbill.getDeptId(), arecbill.getEmpId());
			if(balance == null) {
				//this.initContactBalance(1, period.getFyear(), period.getPeriod(), arecbill.getCustSuppId(), arecbill.getDeptId(), arecbill.getEmpId());
				throw new ServiceException("核销失败，原因：未检索到客户应收余额表的往来余额。");
			}
			cbMapper.recordAccountsReceivableForCredit(balance.getId(), arecbill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_APAYBILL_OFFSET_ARECBILL, arecbill, contact.getId(), period);
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_APAYBILL_OFFSET_ARECBILL, linkcontact, period);
		}
		
		for(RpAPayBill apaybill : apayBills) {
			//arpBillMapper.billCheck(apaybill.getArpBillId(), apaybill.getCurrentCheckAmount());
			RpContact linkcontact = contactMapper.findRpContactByArpBill(apaybill.getArpBillId());
			if(linkcontact == null) {
				throw new ServiceException("更新往来明细的核销金额出错，原因：应付单" + apaybill.getArpBillCode() + "未检索到对应的往来明细。");
			}
			contactMapper.billCheck(linkcontact.getId(), apaybill.getCurrentCheckAmount());
			
			RpContact contact = new RpContact();
			contact.setDirect(-1);
			contact.setTypeId(-7);
			contact.setBillDate(new Date(System.currentTimeMillis()));
			contact.setCustSuppId(apaybill.getCustSuppId());
			contact.setDeptId(apaybill.getDeptId());
			contact.setEmpId(apaybill.getEmpId());
			contact.setAmount(apaybill.getCurrentCheckAmount());
			contact.setCheckStatus(1);
			contact.setRemAmount(BigDecimal.ZERO);
			contact.setFyear(period.getFyear());
			contact.setPeriod(period.getPeriod());
			contact.setRemark("应付款冲抵应收款，对应单据:" + apaybill.getArpBillCode());
			contactMapper.insertContact(contact);
			
			RpContactBalance balance = this.findContactBalance(-1, period.getFyear(), period.getPeriod(), apaybill.getCustSuppId(), apaybill.getDeptId(), apaybill.getEmpId());
			if(balance == null ){
				throw new ServiceException("核销失败，原因：未检索到客户应付余额表的往来余额。");
			}
			cbMapper.recordAccountsPayableForDebit(balance.getId(), apaybill.getCurrentCheckAmount());
			
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_APAYBILL_OFFSET_ARECBILL, apaybill, contact.getId(), period);
			createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_APAYBILL_OFFSET_ARECBILL, linkcontact, period);
		}
		return checkId;
	}
	
	public Long checkForApayBillTransfer(RpAPayBill apayBill,Integer custSuppId) throws ServiceException {
		RpAPayBill transferBill = new RpAPayBill();
		SysAccountPeriod period = getCurrentPeriod();
		Customer customer = custMapper.getCust(custSuppId);
		transferBill.setAmount(apayBill.getCurrentCheckAmount());
		transferBill.setArpDate(apayBill.getArpDate());
		transferBill.setBillDate(new Date(System.currentTimeMillis()));
		transferBill.setCurrentCheckAmount(apayBill.getCurrentCheckAmount());
		transferBill.setCustomer(customer);
		transferBill.setCustSuppId(custSuppId);
		transferBill.setDept(apayBill.getDept());
		transferBill.setDeptId(apayBill.getDeptId());
		transferBill.setEmp(apayBill.getEmp());
		transferBill.setEmpId(apayBill.getEmpId());
		transferBill.setFuncDate(new Date(System.currentTimeMillis()));
		transferBill.setFyear(period.getFyear());
		transferBill.setPeriod(period.getPeriod());
		transferBill.setMakerId(getCurrentUser().getEmpId());
		transferBill.setArpBillCode(newNumber(RpAPayBill.class));
		String outCustName = "";
		if(apayBill.getCustomer() != null) {
			outCustName = "【" + apayBill.getCustomer().getCustomerCode() + "】" + apayBill.getCustomer().getCustomerName();
		} else {
			outCustName = apayBill.getCustSuppId().toString();
		}
		transferBill.setRemark("由客户:" + outCustName +  " 的应付单转入,原单据:" + apayBill.getArpBillCode());

		//transferBill.setRemark("应付单转入:" + apayBill.getArpBillId());
		arpBillMapper.insertArpBill(transferBill);
		archiveAPayBill(transferBill.getArpBillId());
		
		return checkForApayBillTransfer(apayBill, transferBill);
	}

	/* (non-Javadoc)
	 * @see com.thinkgsp.jxc.service.RpBillCheckService#checkForApayBillTransfer(java.util.List, com.thinkgsp.jxc.model.RpAPayBill)
	 */
	private Long checkForApayBillTransfer(RpAPayBill apayBill,RpAPayBill transferBill) throws ServiceException {
		List<RpBillCheck> apaybills = new ArrayList<RpBillCheck>();
		apaybills.add(apayBill);
		List<RpBillCheck> transferCheck = new ArrayList<RpBillCheck>();
		transferCheck.add(transferBill);
		if(!validateCheckAmount(apaybills, transferCheck, false)) {
			throw new ServiceException("核销金额检查不通过。");
		}
		SysAccountPeriod period = getCurrentPeriod();
		
		RpPayBill payBill = new RpPayBill();
		payBill.setCustomer(transferBill.getCustomer());
		payBill.setCustSuppId(apayBill.getCustSuppId());
		payBill.setDeptId(apayBill.getDeptId());
		payBill.setEmpId(apayBill.getEmpId());
		payBill.setMakerId(getCurrentUser().getEmpId());
		payBill.setAmount(apayBill.getCurrentCheckAmount());
		payBill.setFyear(period.getFyear());
		payBill.setPeriod(period.getPeriod());
		payBill.setRemark("应付单转销到客户【" + transferBill.getCustomer().getCustomerCode() + "】" + transferBill.getCustomer().getCustomerName());
		payBill.setRpBillCode(newNumber(RpPayBill.class));
		RpBillDetail detail = new RpBillDetail();
		detail.setArpBillId(apayBill.getArpBillId());
		detail.setSettleAmount(apayBill.getCurrentCheckAmount());
		payBill.getDetails().add(detail);
		savePayBill(payBill);
		if(!payBill.getArchived()) {
			archivePayBill(payBill.getRpBillId());
		}

		payBill.setCurrentCheckAmount(apayBill.getCurrentCheckAmount());
		
		Long checkId = newCheckId();
		//核销付款单
		RpContact contact = contactMapper.findRpContactByRpBill(payBill.getRpBillId());
		if(contact == null) {
			throw new ServiceException("更新往来明细的核销金额出错，原因：付款单" + payBill.getRpBillId() + "未检索到对应的往来明细。");
		}
		contactMapper.billCheck(contact.getId(), payBill.getCurrentCheckAmount());	
		createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_PAYBILL, payBill, contact.getId(), period);

		//核销应付单
		contact = contactMapper.findRpContactByArpBill(apayBill.getArpBillId());
		if(contact == null) {
			throw new ServiceException("更新往来明细的核销金额出错，原因：应付单" + apayBill.getArpBillId() + "未检索到对应的往来明细。");
		}
		contactMapper.billCheck(contact.getId(), apayBill.getCurrentCheckAmount());
		createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_PAYBILL, apayBill, contact.getId(), period);
		
		contact = contactMapper.findRpContactByArpBill(transferBill.getArpBillId());
		createAndSaveRpCheckLog(checkId, RpBillCheckLog.CHECK_TYPE_APAYBILL_TRANSFER, contact, period);
		return checkId;
	}
	
	/**
	 * 反核销
	 * 转销单反核销后需要，手工删除生成的收款单与应收单
	 * @param checkId
	 */
	public void checkForRevoke(Long checkId) throws ServiceException {
		List<RpBillCheckLog> checkLogs = checkLogMapper.getCheckLogListByCheckId(checkId);
		SysAccountPeriod period = this.getCurrentPeriod();
		
		for(RpBillCheckLog checkLog : checkLogs) {
			if(period.getFyear().intValue() != checkLog.getFyear().intValue() 
					|| period.getPeriod().intValue() != checkLog.getPeriod().intValue()) {
				log.debug("unCheck is not current period bill:checkId:" + checkId + ";year:" + checkLog.getFyear() + ";period:" + checkLog.getPeriod());
				throw new ServiceException("不是当期的核销日志，不允许反核销。");
			}
			revokeContact(checkLog);
			revokeContactBal(checkLog);
		}
		checkLogMapper.deleteCheckLogByCheckId(checkId);
	}
	
	private void revokeContact(RpBillCheckLog checkLog) {
		BigDecimal amount = checkLog.getCheckAmount();
		RpContact contact = checkLog.getContact();
		if(contact.getTypeId() == RpContact.CONTACT_TYPE_APAY_OFFSET_AREC ||
				contact.getTypeId() == RpContact.CONTACT_TYPE_AREC_OFFSET_APAY) {
			contactMapper.deleteContact(contact.getId());
		} else {
			//转销单据由于是未核销的，所以不需要反核销
			if(checkLog.getCheckType().intValue() == 4 ||
					checkLog.getCheckType().intValue() == -4) {
				return;
			}
			contactMapper.billUnCheck(contact.getId(), amount);
		}
	}
	
	private void revokeContactBal(RpBillCheckLog checkLog) throws ServiceException {
		RpContactBalance balance = null;
		switch(checkLog.getCheckType()) {
		case RpBillCheckLog.CHECK_TYPE_APAYBILL_OFFSET_ARECBILL:
			balance = this.findContactBalance(-1, checkLog.getFyear(), checkLog.getPeriod(), checkLog.getCustSuppId(), checkLog.getContact().getDeptId(), checkLog.getContact().getEmpId());
			cbMapper.recordAccountsPayableForDebit(balance.getId(), checkLog.getCheckAmount().negate());
			break;
		case RpBillCheckLog.CHECK_TYPE_ARECBILL_OFFSET_APAYBILL:
			balance = this.findContactBalance(1, checkLog.getFyear(), checkLog.getPeriod(), checkLog.getCustSuppId(), checkLog.getContact().getDeptId(), checkLog.getContact().getEmpId());
			cbMapper.recordAccountsReceivableForCredit(balance.getId(), checkLog.getCheckAmount());
			break;
		}
	}
	
	/**
	 * 根据应收应付单创建并保存核销日志
	 * @param checkId
	 * @param checkType
	 * @param arpBill
	 * @param contactId
	 * @param period
	 */
	private void createAndSaveRpCheckLog(Long checkId,int checkType,RpArpBill arpBill,Long contactId,SysAccountPeriod period) {
		RpBillCheckLog checkLog = new RpBillCheckLog();
		checkLog.setCheckId(checkId);
		checkLog.setCheckerId(this.getCurrentUser().getEmpId());
		checkLog.setCustSuppId(arpBill.getCustSuppId());
		checkLog.setContactId(contactId);
		checkLog.setCheckType(checkType);
		checkLog.setDirect(arpBill.getDirect());
		checkLog.setCheckAmount(arpBill.getCurrentCheckAmount());
//		if(arpBill.getDirect().intValue() == 1) {
//			checkLog.setCreditAmt(arpBill.getCurrentCheckAmount());
//		} else {
//			checkLog.setDebitAmt(arpBill.getCurrentCheckAmount());
//		}
		checkLog.setDeptId(arpBill.getDeptId());
		checkLog.setFyear(period.getFyear());
		checkLog.setPeriod(period.getPeriod());
		checkLogMapper.insertCheckLog(checkLog);
	}
	
	/**
	 * 根据收款付款单创建并保存核销日志
	 * @param checkId
	 * @param checkType
	 * @param rpBill
	 * @param contactId
	 * @param period
	 */
	private void createAndSaveRpCheckLog(Long checkId,int checkType,RpBill rpBill,Long contactId,SysAccountPeriod period) {
		RpBillCheckLog checkLog = new RpBillCheckLog();
		checkLog.setCheckId(checkId);
		checkLog.setCheckerId(this.getCurrentUser().getEmpId());
		checkLog.setCustSuppId(rpBill.getCustSuppId());
		checkLog.setContactId(contactId);
		checkLog.setCheckType(checkType);
		checkLog.setDirect(rpBill.getDirect());
		checkLog.setCheckAmount(rpBill.getCurrentCheckAmount());
		checkLog.setDeptId(this.getCurrentUser().getLogonDept().getDeptId());
//		if(rpBill.getDirect().intValue() == -1) {
//			checkLog.setCreditAmt(rpBill.getCurrentCheckAmount());
//		} else {
//			checkLog.setDebitAmt(rpBill.getCurrentCheckAmount());
//		}
		checkLog.setFyear(period.getFyear());
		checkLog.setPeriod(period.getPeriod());
		checkLogMapper.insertCheckLog(checkLog);
	}
	
	/**
	 * 根据往来记录创建并保存核销日志
	 * @param checkId
	 * @param checkType
	 * @param arpBill
	 * @param contactId
	 * @param period
	 */
	private void createAndSaveRpCheckLog(Long checkId,int checkType,RpContact contact,SysAccountPeriod period) {
		RpBillCheckLog checkLog = new RpBillCheckLog();
		checkLog.setCheckId(checkId);
		checkLog.setCheckerId(this.getCurrentUser().getEmpId());
		checkLog.setCustSuppId(contact.getCustSuppId());
		checkLog.setContactId(contact.getId());
		checkLog.setCheckType(checkType);
		checkLog.setDirect(contact.getDirect());
		checkLog.setCheckAmount(contact.getAmount());
//		if(contact.getDirect().intValue() == 1) {
//			checkLog.setCreditAmt(contact.getAmount());
//		} else {
//			checkLog.setDebitAmt(contact.getAmount());
//		}
		checkLog.setDeptId(this.getCurrentUser().getLogonDept().getDeptId());
		checkLog.setFyear(period.getFyear());
		checkLog.setPeriod(period.getPeriod());
		checkLogMapper.insertCheckLog(checkLog);
	}	

	/**
	 * 检查核销单据与被核销单据的金额是否相等
	 * @param arpbill
	 * @param rpbill
	 * @return
	 */
	private boolean validateCheckAmount(List<RpBillCheck> arpbills,
			List<RpBillCheck> rpbills,boolean isRefund) throws ServiceException {
		
		BigDecimal arpBillAmount = BigDecimal.ZERO;
		for(RpBillCheck arpBill : arpbills) {
			BigDecimal chkTotalAmount = arpBill.getCheckedAmount().add(arpBill.getCurrentCheckAmount());
			if (chkTotalAmount.compareTo(arpBill.getBillAmount()) == 1) {
				throw new ServiceException("被核销单据的核销金额:" + chkTotalAmount + "大于单据金额:" + arpBill.getBillAmount());
			}
			arpBillAmount = arpBillAmount.add(arpBill.getCurrentCheckAmount());
		}
		BigDecimal rpBillAmount = BigDecimal.ZERO;
		for(RpBillCheck rpBill : rpbills) {
			BigDecimal chkTotalAmount = rpBill.getCheckedAmount().add(rpBill.getCurrentCheckAmount());
			BigDecimal billAmount = rpBill.getBillAmount();
			if(chkTotalAmount.compareTo(billAmount) == 1) {
				throw new ServiceException("核销单据的核销金额:" + chkTotalAmount + "大于单据金额:" + billAmount);
			}
			rpBillAmount = rpBillAmount.add(rpBill.getCurrentCheckAmount());
		}
		
		if(isRefund) {
			if(BigDecimal.ZERO.compareTo(arpBillAmount.add(rpBillAmount)) == 0) {
				return true;
			} else {
				throw new ServiceException("退款单核销双方之和必须为零。退款单:" + arpBillAmount.toString() + ";收付款单:" + rpBillAmount.toString());
			}
		} else {
			if(arpBillAmount.compareTo(rpBillAmount) == 0) {
				return true;
			} else {
				throw new ServiceException("核销金额:" + rpBillAmount + "与被核销金额:" + arpBillAmount + "不符。");
			}
		}
	}
	
	/**
	 * 创建核销ID
	 * @return
	 */
	private Long newCheckId() throws ServiceException {
//		RpCheckId checkId = new RpCheckId();
//		checkLogMapper.newCheckId(checkId);
//		return checkId.getCheckId();
		Long checkId = numberRoleService.newId(numberRoleConfig.getRpBillCheckIdRoleId());
		return checkId;
	}

}
